Skip to content

feat(#818): make prioritize agent multi-forge (GitHub + GitLab) - #820

Merged
ggallen merged 1 commit into
mainfrom
agent/818-prioritize-multi-forge
Aug 20, 2026
Merged

feat(#818): make prioritize agent multi-forge (GitHub + GitLab)#820
ggallen merged 1 commit into
mainfrom
agent/818-prioritize-multi-forge

Conversation

@fullsend-ai-coder

@fullsend-ai-coder fullsend-ai-coder Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Convert the prioritize agent from GitHub-only to multi-forge (GitHub + GitLab) support, following the established 6-layer pattern from the triage agent conversion
  • Add GitLab ops library with idempotent sticky comments; custom fields API integration deferred (the GitLab Issues API silently ignores unknown keys, and the correct Custom Fields API requires runtime field ID resolution only available on Premium/Ultimate — scores are always available in the reasoning comment)
  • Split env, policy, and harness config into per-forge blocks; create forge-dispatch ops library and update pre/post scripts to use forge_*() functions

Changes by layer

  1. Agent prompt (agents/prioritize.md): forge-neutral — ISSUE_URL, curl in tools, forge skill dispatch
  2. Harness (harness/prioritize.yaml): remove top-level policy:, add forge.github/forge.gitlab blocks
  3. Env files: env/github/prioritize.env and env/gitlab/prioritize.env
  4. Policies: policies/github/prioritize.yaml (gh binary) and policies/gitlab/prioritize.yaml (curl binary)
  5. Ops library: dispatcher + GitHub ops (extracted from post-script) + GitLab ops (new)
  6. Scripts: pre/post scripts source ops lib and dispatch via forge_*() functions
  7. Tests: existing tests tagged FULLSEND_FORGE=github, new GitLab section with sticky comment, host validation, and spoofed-note rejection coverage
  8. Docs: mention GitLab support, document custom fields deferral

GitLab custom fields deferral

forge_update_project_scores() on GitLab is an honest stub — it logs a notice and returns success. The reasoning:

  • The GitLab Issues API ignores unknown top-level keys (returns 200 silently), so writing RICE field names to it is a no-op
  • The correct Custom Fields API requires field IDs resolved at runtime and is only available on Premium/Ultimate
  • A proper implementation needs a setup script (analogous to setup-prioritize.sh for GitHub Projects V2) to create and resolve custom field IDs
  • Scores are always available in the reasoning comment posted via forge_post_sticky_comment, so no information is lost

This is tracked as a follow-up for when GitLab Custom Fields API support is needed.

Testing

  • All existing GitHub tests pass with FULLSEND_FORGE=github
  • New GitLab tests: happy path, no-gh-calls, comment posting, timeout flags, custom fields stub, host validation rejection, sticky comment idempotency (spoofed notes, own-note update, history preservation)
  • make script-build and make check-bundle pass
  • shellcheck passes with repo config (-e SC1091,SC2001,SC2016)
  • gitlint passes

Closes #818

Post-script verification

  • Branch is not main/master (agent/818-prioritize-multi-forge)
  • Secret scan passed (gitleaks — 693ed83bbd5289e1ef13d6c7a3772f61efc43c86..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 15, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:20 PM UTC · Completed 8:36 PM UTC

Commit: 4c0b4fc · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 15, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [permission-expansion] policies/gitlab/prioritize.yaml — The GitLab sandbox policy declares access: read-only for gitlab_api endpoints, but curl is in the binary allowlist and GITLAB_TOKEN is injected into the sandbox via env.sandbox. Whether access: read-only on REST endpoints blocks non-GET HTTP methods depends on the sandbox runtime's enforcement. On GitHub, this is mitigated by excluding curl from the binary allowlist; GitLab cannot do the same since curl is the only API client. Verify that the sandbox runtime enforces HTTP method restrictions for REST endpoints with access: read-only.

  • [protected-path] agents/, harness/, policies/, scripts/ — 13 files under protected paths are modified. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and provides detailed justification for the changes. Human approval is required for protected-path changes regardless of context.

Low

  • [breaking-api] harness/prioritize.yaml — The env var exposed to runner/sandbox scripts changed from GITHUB_ISSUE_URL to ISSUE_URL. Downstream repos extending this harness via base: composition that reference the old name in custom scripts will need to update. Documented in migration notes in docs/prioritize.md.

  • [architectural-conflict] harness/prioritize.yamlpre_script/post_script are at the top level only, not repeated in each forge.<platform> section as in harness/triage.yaml. Tests pass for both forges; this is a consistency observation.

  • [naming-convention] scripts/lib/prioritize-ops.lib.sh — Function prefix is forge_ vs triage's tracker_. Deliberate divergence — triage also supports Jira (not a forge).

  • [error-handling-idiom] scripts/lib/gitlab-prioritize-ops.lib.sh — Several defensive improvements over the triage pattern: _gitlab_api() validates GITLAB_HOST per call (defense-in-depth), error messages use _gha_sanitize() on user-controlled values (GHA command injection protection), and forge_post_sticky_comment emits ::warning:: annotations on failures (operator visibility). Consider backporting these improvements to triage ops.

  • [Host Allowlist Scope] scripts/lib/gitlab-prioritize-ops.lib.sh — Host allowlist (gitlab.com, gitlab.cee.redhat.com) maintained in three locations (_gitlab_api(), forge_validate_issue_url(), policies/gitlab/prioritize.yaml). Documented in docs/prioritize.md under "GitLab host allowlist".

  • [provenance-warning] Prior review context discarded: provenance validation failed (unverifiable-wrong-app). This review treats all findings as first-time assessments.

Previous run

Review

Findings

Medium

  • [protected-path] agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/ (13 files) — This PR modifies files under protected paths (agents/, harness/, policies/, scripts/). The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and explains the rationale (multi-forge conversion following the established 6-layer pattern). Human approval is required for protected-path changes regardless of context.

  • [error handling] scripts/lib/gitlab-prioritize-ops.lib.sh:79 — The forge_post_sticky_comment function's primary POST and PUT paths (for creating/updating the GitLab sticky note) redirect output to /dev/null without explicit error handling. Under set -e, a curl failure would abort the script with an unhelpful error rather than providing a graceful warning like the fallback path does (which emits ::warning::Failed to post fallback comment). Adding || { echo '::warning::Failed to post/update comment' >&2; return 1; } would improve error observability.

Low

  • [edge-case] scripts/lib/gitlab-prioritize-ops.lib.sh:67 — The stripped_old sed command deletes the marker line only if it exactly matches as the sole content on line 1. While the code always constructs marked_body with the marker on its own line, a manually edited note could accumulate duplicate markers in the history <details> block.

  • [permission-expansion] agents/prioritize.md:4 — The agent frontmatter tool grant expands from Bash(gh,jq) to Bash(gh,curl,jq). Defense-in-depth is adequate: the GitHub policy binary allowlist excludes **/curl, and the GitLab policy restricts it to two allowlisted hosts with read-only access.

  • [injection] scripts/lib/gitlab-prioritize-ops.lib.sh:99 — The sticky comment pagination loop can accumulate up to 5000 notes (50 pages × 100 per page) in memory. Practical risk is low given typical issue comment volumes, but the unbounded accumulation has no early-exit optimization when the target note is found.

  • [test-coverage] scripts/post-prioritize-test.sh:793 — No GitHub forge smoke test runs after the GitLab test section restores GitHub state, which would catch state leakage if future tests are added after the restore block.

  • [naming-convention-inconsistency] scripts/lib/gitlab-prioritize-ops.lib.sh:32 — The _gitlab_api() function includes an inline GITLAB_HOST allowlist check not present in the equivalent triage or review ops libraries. This is a defense-in-depth improvement but diverges from the established function shape.

Previous run (2)

Review

Findings

Medium

  • [protected-path] agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/ (13 files) — This PR modifies files under protected paths (agents/, harness/, policies/, scripts/). The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and the description provides clear rationale for the changes (multi-forge conversion following the established 6-layer pattern). Human approval is always required for protected-path changes, regardless of context.

Low

  • [consistency-deviation] harness/prioritize.yaml — The forge.github and forge.gitlab sections omit pre_script and post_script entries. Every other multi-forge harness in this repo (triage.yaml, review.yaml, code.yaml) duplicates these inside each forge section. The top-level entries work correctly since the scripts dispatch internally via FULLSEND_FORGE, but this deviates from the established convention.
    Remediation: Add pre_script: scripts/pre-prioritize.sh and post_script: scripts/post-prioritize.sh to both forge sections.

  • [dead-code] scripts/post-prioritize-test.sh:792 — The GitLab test setup runs unset GITHUB_ISSUE_URL but this variable was renamed to ISSUE_URL earlier in the file (line 770). The unset is a no-op — dead code from the variable rename.
    Remediation: Remove the unset GITHUB_ISSUE_URL line.

  • [naming-convention] scripts/lib/gitlab-prioritize-ops.lib.sh:32 — The _gitlab_api function includes host allowlist validation (case statement checking GITLAB_HOST) on every API call. In the established pattern (gitlab-triage-ops.lib.sh, gitlab-review-ops.lib.sh), the _gitlab_api helper is a thin curl wrapper with no host validation — the check lives exclusively in the entry-point validate function. This is defense-in-depth rather than an error, but it creates a convention divergence and duplicated control logic.

Previous run (3)

Review

Findings

Medium

  • [protected-path] agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/ (9 files) — 13 of 19 changed files fall under protected paths (agents/, harness/, policies/, scripts/). The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and explains the rationale (multi-forge conversion following the established 6-layer pattern). Human approval is always required for protected-path changes.
  • [pattern-violation] harness/prioritize.yaml — The forge.github and forge.gitlab sections omit pre_script and post_script entries. Other multi-forge harnesses (triage, code, review) duplicate them in each forge section. The PR removed the forge-level entries that existed in the base branch. Functionally correct today but diverges from the established pattern.
    Remediation: Add pre_script and post_script inside both forge sections.

Low

  • [workflow-command-injection] scripts/lib/github-prioritize-ops.lib.sh:82 — Error message outputs ${ISSUE_URL} without _gha_sanitize, inconsistent with the sanitized error path at line 29 in the same file. Minimal risk (URL is regex-validated before this point), but the inconsistency creates a maintenance hazard.
    Remediation: Use $(_gha_sanitize "${ISSUE_URL}").
  • [error-handling-idiom] scripts/lib/gitlab-prioritize-ops.lib.sh:42_gitlab_api() validates GITLAB_HOST against the allowlist on every call, unlike the equivalent in gitlab-triage-ops.lib.sh. This is defense-in-depth (an improvement over the triage pattern) but diverges from the established convention.
  • [error-handling-idiom] scripts/lib/gitlab-prioritize-ops.lib.sh:80 — Fallback path in forge_post_sticky_comment emits ::warning:: annotations; the triage equivalent silently falls through. The new behavior improves operator visibility into degraded comment posting.
  • [edge-case] scripts/lib/gitlab-prioritize-ops.lib.sh:100 — Marker-stripping sed pattern in forge_post_sticky_comment requires exact first-line match; trailing whitespace would cause marker duplication in the Previous run history block. Matches the triage-ops implementation; cosmetic risk only.
  • [error-handling] scripts/lib/gitlab-prioritize-ops.lib.sh:69 — Notes pagination aggregates via jq -s 'add', which is correct for arrays but fragile against non-array API responses. The || break fallback on batch fetch provides adequate mitigation.
Previous run (4)

Review

Findings

Medium

  • [breaking-env-var-rename] harness/prioritize.yaml:54 — The env var passed to runner and sandbox under forge.github.env changed from GITHUB_ISSUE_URL to ISSUE_URL. Downstream repos using base: composition with custom scripts referencing $GITHUB_ISSUE_URL will silently receive an empty value. The mapping ISSUE_URL: ${GITHUB_ISSUE_URL} preserves the workflow-level input, but the env var name visible inside scripts has changed. This follows the same transition already completed by the triage agent and is documented in the migration notes.
    Remediation: Consider passing both GITHUB_ISSUE_URL and ISSUE_URL during a deprecation period.

  • [new-required-env-var] scripts/post-prioritize.src.sh:16 — Both pre- and post-prioritize scripts now hard-require FULLSEND_FORGE via ${FULLSEND_FORGE:?}. Direct invocations outside the standard harness flow without setting this variable will fail immediately. The harness sets it automatically via forge-level env blocks.
    Remediation: Consider defaulting FULLSEND_FORGE to github when unset to preserve backward compatibility for GitHub-only consumers.

  • [token-exposure-via-curl] scripts/lib/gitlab-prioritize-ops.lib.sh:30GITLAB_TOKEN is passed via --header 'PRIVATE-TOKEN: ...' on every curl invocation, making it visible in /proc/*/cmdline process listings. This matches the established pattern in all other GitLab ops files (gitlab-triage-ops.lib.sh, gitlab-review-ops.lib.sh) and is mitigated by sandbox process isolation.
    Remediation: Consider --header @file or .netrc approach to avoid token in process arguments.

  • [protected-path] — 14 files under protected governance paths are modified: agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, and 10 files under scripts/. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and the description explains the multi-forge conversion rationale comprehensively. Human approval is required for protected-path changes regardless of context.

Low

  • [breaking-structural-change] harness/prioritize.yaml:39 — Forge-level pre_script/post_script entries were removed; scripts are now declared only at the top level. The scripts dispatch internally via FULLSEND_FORGE, making top-level-only placement correct for this design. This diverges from the triage harness (both levels) and review harness (forge-level only), but neither pattern is authoritative.

  • [error-handling] scripts/lib/gitlab-prioritize-ops.lib.sh:80 — Error handling asymmetry in forge_post_sticky_comment: the primary path relies on set -e for API failures (fatal), while the fallback path explicitly catches and tolerates failures. This is arguably correct design — the fallback is best-effort recovery when bot username resolution already failed.

  • [comment-body-dos] scripts/lib/gitlab-prioritize-ops.lib.sh:108 — History accumulation in sticky comments grows toward 60KB per run via nested <details> blocks. The existing max_len=60000 truncation guard prevents unbounded growth but does not strip nested history from prior runs.

  • [code-organization] harness/prioritize.yaml:28pre_script/post_script at top-level only, while peer harnesses use varying patterns. Top-level only is valid and cleaner for scripts that handle forge dispatch internally.

  • [api-shape-pattern] scripts/lib/gitlab-prioritize-ops.lib.sh:20_gitlab_api() includes an inline host allowlist check absent from peer GitLab ops files. This provides defense-in-depth (host is also validated in forge_validate_issue_url()) but creates a consistency gap with peers.

  • [edge-case] scripts/lib/gitlab-prioritize-ops.lib.sh:68 — Marker removal via anchored sed regex (/^marker$/d) may fail if the stored marker line has trailing whitespace, causing cosmetic duplication of the marker in the history <details> block.

  • [test-adequacy] scripts/post-prioritize-test.sh:789 — No test verifies that FULLSEND_FORGE=gitlab with an unset GITLAB_TOKEN triggers the expected ${GITLAB_TOKEN:?} guard error.

  • [agent-prompt-contract-change] agents/prioritize.md:104 — Input contract changed from GITHUB_ISSUE_URL to ISSUE_URL; explicit gh issue view command replaced with forge-skill dispatch. Documented in docs/prioritize.md migration notes.

Previous run (5)

Review

Findings

Medium

  • [protected-path] agents/, harness/, policies/, scripts/ — 13 files under governance/infrastructure paths are modified. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and provides detailed rationale for the multi-forge conversion. Human approval is required for protected-path changes regardless of context.

  • [naming-convention] scripts/lib/gitlab-prioritize-ops.lib.sh:32 — The _gitlab_api helper adds a host allowlist guard and a GITLAB_HOST emptiness check inside the function body. The established pattern in peer files (gitlab-triage-ops.lib.sh, gitlab-review-ops.lib.sh) keeps _gitlab_api as a thin curl wrapper with no host validation — host validation lives exclusively in forge_validate_issue_url. Adding a second validation site creates a maintenance burden: if a new GitLab host is added, it must be updated in both forge_validate_issue_url and _gitlab_api.
    Remediation: Remove the host allowlist check and GITLAB_HOST emptiness guard from _gitlab_api() to match peer files.

  • [breaking-env-var-rename] harness/prioritize.yaml — The env vars consumed by pre/post scripts have been renamed from GITHUB_ISSUE_URL to ISSUE_URL in env.runner and env.sandbox. Downstream repositories using base: composition with custom pre/post scripts that reference GITHUB_ISSUE_URL directly will get an empty value. The migration notes in docs/prioritize.md document this change.

  • [new-required-env-var] harness/prioritize.yamlFULLSEND_FORGE is now required by pre/post scripts. The harness sets it automatically under each forge section, but base: composition overrides that strip forge sections without setting FULLSEND_FORGE explicitly will fail at script startup. Documented in migration notes.

Low

  • [GHA-injection-inconsistency] scripts/lib/gitlab-prioritize-ops.lib.sh:93 — The forge_validate_issue_url error message outputs ${host} without _gha_sanitize, while the preceding error (line 80) sanitizes ISSUE_URL. The host is regex-validated to [a-zA-Z0-9._-]+ before reaching this path, preventing exploitation, but the inconsistency with the GitHub-side validator (which sanitizes) is worth addressing for defense-in-depth.
    Remediation: echo "ERROR: GitLab host '$(_gha_sanitize "${host}")' is not in the allowed host list" >&2

  • [incomplete-doc] docs/prioritize.md — The PR adds GitLab support with a hardcoded host allowlist in gitlab-prioritize-ops.lib.sh and policies/gitlab/prioritize.yaml, but docs/prioritize.md does not document the allowlist or how to support self-hosted GitLab. The analogous docs/code.md has a "GitLab host allowlist" bullet in its multi-forge section.

  • [pattern-deviation] harness/prioritize.yaml — The triage harness specifies pre_script/post_script both at the top level and inside each forge.<platform> block. The prioritize harness specifies them only at the top level. Whether this is functionally equivalent depends on harness engine merge semantics.

  • [env-var-behavioral-change] scripts/post-prioritize.src.shORG and PROJECT_NUMBER were previously hard-required (: "${ORG:?}"); now they are soft-optional in forge_update_project_scores (emits ::notice:: and returns 0 if unset). This is intentional for multi-forge support but the migration notes do not mention the behavioral change.

  • [tool-scope-expansion] agents/prioritize.md:4tools expanded from Bash(gh,jq) to Bash(gh,curl,jq). The GitHub network policy binary allowlist excludes **/curl, blocking its use on GitHub. This is a layered-defense approach documented in both the agent frontmatter and the policy file.

Previous run (6)

Review

Findings

Medium

  • [protected-path] agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/lib/github-prioritize-ops.lib.sh, scripts/lib/gitlab-prioritize-ops.lib.sh, scripts/lib/prioritize-ops.lib.sh, scripts/post-prioritize-test.sh, scripts/post-prioritize.sh, scripts/post-prioritize.src.sh, scripts/pre-prioritize-test.sh, scripts/pre-prioritize.sh, scripts/pre-prioritize.src.sh — 13 files under protected paths (agents/, harness/, policies/, scripts/) are modified. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 which authorizes the multi-forge conversion, and the description explains the rationale. Human approval is always required for protected-path changes regardless of context.

  • [pattern-divergence] harness/prioritize.yaml — The triage harness has pre_script/post_script in both the top-level section AND each forge.<platform> section. This PR removes them from forge.github (where they existed before) and does not add them to the new forge.gitlab section, keeping them only at the top level. This diverges from the established triage multi-forge pattern. If the harness engine requires forge-level script declarations to override or supplement top-level ones, scripts might not run correctly.
    Remediation: Add pre_script/post_script to both forge.github and forge.gitlab sections to match the triage pattern, or verify that the harness engine's merge semantics support top-level-only placement.

  • [error-handling-gap] scripts/lib/gitlab-prioritize-ops.lib.sh — In forge_post_sticky_comment, the fallback path when _gitlab_bot_username fails posts a non-sticky comment via _gitlab_api POST but doesn't check the exit status. If the fallback POST itself fails, the bare return propagates the non-zero exit code, causing the caller to die under set -e without a clear error message indicating it was the fallback path that failed.
    Remediation: Add explicit error checking: _gitlab_api POST ... > /dev/null || echo '::warning::Failed to post fallback comment' >&2

Low

  • [logic-error] scripts/lib/gitlab-prioritize-ops.lib.sh — The stripped_old logic in forge_post_sticky_comment uses sed to remove the marker from line 1 of the old note body. When the old body was only the marker with no real content, stripped_old will still be non-empty (due to echo's trailing newline), causing an empty <details> block to be appended. Cosmetic; the scenario is unlikely in practice since the old body will always contain RICE scores.

  • [naming-convention] scripts/lib/gitlab-prioritize-ops.lib.sh_gitlab_api() includes an inline host allowlist check and a GITLAB_HOST-is-set guard that are not present in the peer implementation (gitlab-triage-ops.lib.sh). This is defense-in-depth rather than a bug, but diverges from the established thin-wrapper pattern.

  • [injection] scripts/lib/gitlab-prioritize-ops.lib.shGITLAB_HOST is echoed without _gha_sanitize in _gitlab_api's rejection error. Mitigated: the value is constrained by the URL regex to [a-zA-Z0-9._-]+ (excludes :: sequences) and output goes to stderr, not GHA workflow commands.

  • [edge-case] scripts/lib/gitlab-prioritize-ops.lib.sh — No retry logic for transient network failures in _gitlab_api, unlike the GitHub CSMA/CD path. Consistent with the triage GitLab ops which also has no retries.

  • [test-coverage] scripts/post-prioritize-test.sh — GitHub-specific env vars (ORG, PROJECT_NUMBER) are not unset in the GitLab test section. While the GitLab code path doesn't use these (the forge_update_project_scores stub returns immediately), leaked vars mean a future regression could go undetected.

  • [authorization] agents/prioritize.mdcurl is granted at the tool layer for both forges. On GitHub, the network policy binary allowlist (policies/github/prioritize.yaml) excludes **/curl, providing defense-in-depth. The comment in the agent frontmatter correctly documents this layered approach.

  • [naming-convention] scripts/pre-prioritize.src.sh — Verbose shellcheck disable comment compared to the terser pattern in peer .src.sh files.

Previous run (7)

Review

Findings

Medium

  • [test-inadequate] scripts/pre-prioritize.src.sh — The new pre-prioritize.src.sh introduces non-trivial forge-dispatch logic (sourcing prioritize-ops.lib.sh, calling forge_validate_issue_url per forge, host allowlist enforcement) but has no dedicated test file. Every other agent with a non-trivial pre-script has a corresponding test: triage (pre-triage-test.sh), code (pre-code-test.sh), review (pre-review-test.sh), scribe (pre-scribe-test.sh). The Makefile script-test target has no pre-prioritize-test.sh entry.
    Remediation: Add scripts/pre-prioritize-test.sh covering: valid GitHub/GitLab URLs pass, malformed URLs rejected, non-allowlisted GitLab host rejected, unset FULLSEND_FORGE fails. Add the test to the Makefile script-test target.

  • [protected-path] agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/lib/github-prioritize-ops.lib.sh, scripts/lib/gitlab-prioritize-ops.lib.sh, scripts/lib/prioritize-ops.lib.sh, scripts/post-prioritize-test.sh, scripts/post-prioritize.sh, scripts/post-prioritize.src.sh, scripts/pre-prioritize.sh, scripts/pre-prioritize.src.sh — 12 of 18 changed files are under protected paths (agents/, harness/, policies/, scripts/). The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and the description explains the rationale (multi-forge conversion following the established 6-layer pattern). Human approval is required for protected-path changes regardless of context.

Low

  • [pattern-inconsistency] scripts/lib/gitlab-prioritize-ops.lib.sh:32 — The _gitlab_api function includes an inline host allowlist check (case "${GITLAB_HOST}" in gitlab.com|gitlab.cee.redhat.com). The established pattern in gitlab-triage-ops.lib.sh and gitlab-review-ops.lib.sh places no host validation inside _gitlab_api — both delegate that to the URL validation function. Duplicating the allowlist creates a maintenance risk if a new GitLab host is added.

  • [input-validation] scripts/lib/gitlab-prioritize-ops.lib.sh:42 — The forge_validate_issue_url() regex would syntactically accept hostnames like gitlab.com.evil.com in the first pass. This is mitigated by the subsequent exact-match allowlist check, so defense-in-depth is effective.

  • [error-handling-idiom] scripts/lib/gitlab-prioritize-ops.lib.sh:27 — The _gitlab_api function includes a GITLAB_HOST-is-set guard not present in the equivalent function in other GitLab ops files. This is a defensive addition that improves error clarity.

  • [code-organization] harness/prioritize.yaml — The GitHub forge section redeclares pre_script and post_script identically to the top-level declarations. These could be removed from the top level since forge sections always override them.

Previous run (8)

Review

Findings

Medium

  • [protected-path] harness/prioritize.yaml — This PR modifies 12 files under protected governance paths (agents/, harness/, policies/, scripts/): agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/lib/github-prioritize-ops.lib.sh, scripts/lib/gitlab-prioritize-ops.lib.sh, scripts/lib/prioritize-ops.lib.sh, scripts/post-prioritize-test.sh, scripts/post-prioritize.sh, scripts/post-prioritize.src.sh, scripts/pre-prioritize.sh, scripts/pre-prioritize.src.sh. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and provides a detailed rationale for the multi-forge conversion. Human approval is always required for protected-path changes, regardless of context.

Low

  • [naming-convention] scripts/lib/gitlab-prioritize-ops.lib.sh:32 — The _gitlab_api() function adds a host allowlist check inside the API helper, diverging from the established pattern in gitlab-triage-ops.lib.sh and gitlab-review-ops.lib.sh where _gitlab_api() is a thin curl wrapper and host validation lives exclusively in the URL validation function. This is defense-in-depth and not a bug, but creates a structural inconsistency across forge ops libraries.
    Remediation: Consider moving the host allowlist check out of _gitlab_api() to align with the existing pattern.

  • [backward-compatibility] harness/prioritize.yaml:38 — The sandbox env var consumed by scripts changed from GITHUB_ISSUE_URL to ISSUE_URL, and a forge-level policy was added under forge.github. Downstream repos using base: composition that reference GITHUB_ISSUE_URL in custom scripts or depend on the top-level policy may need updates. Migration notes in docs/prioritize.md document both changes.

  • [error-handling-gap] scripts/lib/gitlab-prioritize-ops.lib.sh:82 — The GitLab forge_post_sticky_comment function does not validate that GITLAB_TOKEN is set before making API calls. The GitHub counterpart has an explicit : "${GH_TOKEN:?GH_TOKEN must be set}" guard. If GITLAB_TOKEN is unset, curl sends an empty PRIVATE-TOKEN header resulting in an opaque HTTP 401 rather than a clear error message. Consistent with the triage agent's GitLab ops pattern.
    Remediation: Add : "${GITLAB_TOKEN:?GITLAB_TOKEN must be set}" at the start of forge_post_sticky_comment.

  • [edge-case] scripts/lib/gitlab-prioritize-ops.lib.sh:100 — The GitLab forge_post_sticky_comment function has no retry logic for transient API failures, unlike the GitHub path which benefits from CSMA retry/resilience. Under set -e, a curl failure aborts the script without retry. Consistent with the triage agent's GitLab ops pattern.
    Remediation: Consider adding basic retry logic for the GitLab API calls.

Previous run (9)

Review

Findings

Medium

  • [protected-path] agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/lib/github-prioritize-ops.lib.sh, scripts/lib/gitlab-prioritize-ops.lib.sh, scripts/lib/prioritize-ops.lib.sh, scripts/post-prioritize-test.sh, scripts/post-prioritize.sh, scripts/post-prioritize.src.sh, scripts/pre-prioritize.sh, scripts/pre-prioritize.src.sh — 12 of 18 changed files are under protected paths (agents/, harness/, policies/, scripts/). The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and provides detailed rationale for the multi-forge conversion following the established 6-layer pattern. Human approval is always required for protected-path changes, regardless of context.

Low

  • [error-handling-idiom] scripts/lib/gitlab-prioritize-ops.lib.sh:32_gitlab_api() contains a GITLAB_HOST allowlist case statement not present in the equivalent function in gitlab-triage-ops.lib.sh or gitlab-review-ops.lib.sh. This provides defense-in-depth but deviates from the convention of centralizing host validation in forge_validate_issue_url().

  • [error-handling-idiom] scripts/lib/github-prioritize-ops.lib.sh:37forge_validate_issue_url() echoes the raw ISSUE_URL into its error message without _gha_sanitize() wrapping. The review-ops convention uses _gha_sanitize() for error messages. Same applies to gitlab-prioritize-ops.lib.sh. Note: the codebase is inconsistent — github-triage-ops.lib.sh also omits sanitization.

  • [test-coverage] Makefile — No pre-prioritize-test.sh exists. The new pre-prioritize.src.sh introduces forge-dispatch logic, URL validation, and _gha_sanitize annotation formatting but has no dedicated tests. Pre-test scripts exist for triage, code, review, and scribe agents. Core functions get transitive coverage via post-prioritize-test.sh.

  • [naming-convention] scripts/lib/prioritize-ops.lib.sh:12 — Uses forge_* function names while the triage agent (closest analog for issue-oriented work) uses tracker_*. Consistent with review-ops and code-ops; intentional since prioritize does not support non-forge trackers like Jira.

  • [fail-open] scripts/lib/gitlab-prioritize-ops.lib.sh:63 — When _gitlab_bot_username() fails, forge_post_sticky_comment falls back to non-sticky comment posting. Repeated runs could create duplicates instead of updating in place. Not a security issue; the alternative (failing hard) would prevent any score output.

Info

  • [provenance-warning] — Prior review context discarded: provenance validation failed (unverifiable-wrong-app). This review treats all findings as first-time assessments.
Previous run (10)

Review

Findings

Medium

  • [protected-path] agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/lib/github-prioritize-ops.lib.sh, scripts/lib/gitlab-prioritize-ops.lib.sh, scripts/lib/prioritize-ops.lib.sh, scripts/post-prioritize-test.sh, scripts/post-prioritize.sh, scripts/post-prioritize.src.sh, scripts/pre-prioritize.sh, scripts/pre-prioritize.src.sh — This PR modifies 12 files under protected paths (agents/, harness/, policies/, scripts/). The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and clearly explains the rationale (multi-forge conversion following the established 6-layer pattern). Human approval is always required for protected-path changes, regardless of context.

Low

  • [naming-convention] scripts/post-prioritize.src.sh:2 — File-level comment says # post-prioritize.src.sh (includes .src), but the established convention in other .src.sh files references the generated output filename without the .src suffix. For example, post-triage.src.sh says # post-triage.sh, and this PR's own pre-prioritize.src.sh correctly says # pre-prioritize.sh.
    Remediation: Change line 2 to # post-prioritize.sh — Write RICE scores to the project board and post a reasoning comment.

  • [edge-case] scripts/lib/gitlab-prioritize-ops.lib.sh:119forge_post_sticky_comment() falls back to posting a new comment (without sticky-update logic) when _gitlab_bot_username() fails. Repeated failures could accumulate duplicate comments on the issue. This is not a security or functional issue since the fallback only creates new notes.
    Remediation: Consider logging a warning when falling back to non-sticky comment posting to aid debugging.

Previous run (11)

Review

Findings

Medium

  • [protected-path] harness/prioritize.yaml — This PR modifies 12 files under protected paths (agents/, harness/, policies/, scripts/): agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/lib/github-prioritize-ops.lib.sh, scripts/lib/gitlab-prioritize-ops.lib.sh, scripts/lib/prioritize-ops.lib.sh, scripts/post-prioritize-test.sh, scripts/post-prioritize.sh, scripts/post-prioritize.src.sh, scripts/pre-prioritize.sh, scripts/pre-prioritize.src.sh. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and the description explains the rationale (multi-forge conversion following the established 6-layer pattern). Human approval is always required for protected-path changes, regardless of context.

  • [stale-doc-section] docs/prioritize.md:63 — The Variables section says "None" but the prioritize agent now requires FULLSEND_FORGE and ISSUE_URL. Other multi-forge agent docs (docs/review.md, docs/code.md) list FULLSEND_FORGE in their Variables table.
    Remediation: Add a Variables table matching peer agent docs, listing FULLSEND_FORGE with description and valid values.

  • [env-var-contract] harness/prioritize.yaml:56 — The runner and sandbox env var consumed by scripts changed from GITHUB_ISSUE_URL to ISSUE_URL. Downstream repos using base: composition that reference GITHUB_ISSUE_URL in custom scripts will find it unset. The external caller contract (workflow-level input) is preserved. Migration notes in docs/prioritize.md document this change.
    Remediation: Consider setting GITHUB_ISSUE_URL as a deprecated alias in the GitHub forge env section for one release cycle.

Low

  • [env-var-parity] env/gitlab/prioritize.env — The GitLab env file is missing FULLSEND_FORGE="gitlab" while the GitHub env file includes it. The harness sets it via env blocks, so this is cosmetic. The codebase is inconsistent across agents (code agent includes it in both; triage/review omit it from both).

  • [pattern-deviation] scripts/lib/gitlab-prioritize-ops.lib.sh:22 — The _gitlab_api function includes a GITLAB_HOST allowlist check that other agents' _gitlab_api functions do not have. This is defense-in-depth (not a bug), but deviates from the established pattern where host validation lives only in forge_validate_*_url.

  • [harness-structure] harness/prioritize.yaml:40policy, skills, and host_files are now defined inside forge blocks rather than at the top level. Documented in migration notes. This is the intended design for multi-forge harness composition.

  • [function-naming] scripts/lib/gitlab-prioritize-ops.lib.sh:22 — The helper _gitlab_api shares its name with identically-named functions in other agents' ops libraries. The bundler prevents runtime conflicts, but _gitlab_code_api in gitlab-code-ops.lib.sh uses a prefixed name to avoid ambiguity.

  • [test-adequacy] scripts/post-prioritize-test.sh — No pre-prioritize-test.sh exists. The GitLab URL validation and _gha_sanitize paths in the new pre-script are untested from the pre-script entrypoint.

  • [test-cleanup] scripts/post-prioritize-test.sh:136GITHUB_ISSUE_URL is still exported in the test setup as a leftover from the old API. The post-script now uses ISSUE_URL.

  • [env-var-contract] harness/prioritize.yaml:60FULLSEND_FORGE is now a required env var for pre/post scripts. Previously the prioritize scripts did not depend on this variable.

  • [tool-grant-widening] agents/prioritize.md:7tools: changed from Bash(gh,jq) to Bash(gh,curl,jq). On GitHub, the network policy binary allowlist excludes **/curl. Defense-in-depth is correctly applied.

Previous run (12)

Review

Findings

Medium

  • [pattern-inconsistency] scripts/lib/gitlab-prioritize-ops.lib.sh:26_gitlab_api() includes a host allowlist guard (case "${GITLAB_HOST}") inside the API helper itself. In the established codebase pattern (gitlab-triage-ops.lib.sh, gitlab-review-ops.lib.sh, gitlab-code-ops.lib.sh), _gitlab_api() is a thin curl wrapper with no host validation — host validation lives exclusively in forge_validate_*_url() functions called at script startup. The extra guard is defense-in-depth but breaks the consistent pattern, which could cause maintenance confusion.
    Remediation: Remove the case "${GITLAB_HOST}" guard from _gitlab_api() to match the existing thin-wrapper pattern, or add the same guard to the other agents' _gitlab_api() functions for consistency.

  • [protected-path] This PR modifies files under protected paths: agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, and 8 files under scripts/. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 which authorizes these changes and the description provides clear rationale. Human approval is always required for protected-path changes, regardless of context.

Low

  • [test-inadequate] scripts/post-prioritize-test.sh:720 — The GitLab test suite does not unit-test forge_parse_issue_url extraction with subgroup URLs (e.g., gitlab.com/a/b/c/project/-/issues/42). Current tests only use a simple two-segment path (test-group/test-project).

  • [missing-test] Makefile:444 — No pre-prioritize-test.sh exists in the test suite. Other agents have dedicated pre-script tests (e.g., pre-triage-test.sh). The pre-script's URL validation is indirectly covered by post-script tests, but the _gha_sanitize output in the ::notice:: line is untested.

  • [error-handling-gap] scripts/lib/gitlab-prioritize-ops.lib.sh:43_gitlab_api uses GITLAB_HOST without an explicit set-check. If called before forge_parse_issue_url sets the variable, the empty value falls through to the * case with a confusing error message. The runtime ordering in both pre-prioritize.src.sh and post-prioritize.src.sh is safe (parse runs first), and set -euo pipefail would catch an unset variable.

  • [authorization-spoofing] scripts/lib/gitlab-prioritize-ops.lib.sh — When _gitlab_bot_username fails, forge_post_sticky_comment falls back to creating a new comment without author-filtering existing notes, bypassing spoofing protection. Impact is limited to duplicate comments rather than content injection.

  • [naming-convention] scripts/lib/github-prioritize-ops.lib.sh:29 — The forge_ prefix is used for dispatch functions. The triage agent (closest issue-centric analog) uses the tracker_ prefix, while code/review agents use forge_. The choice aligns with the majority pattern; triage's tracker_ prefix is the outlier (it also supports Jira).

  • [env-file-content] env/github/prioritize.env:1 — Does not include export FULLSEND_FORGE="github", whereas the analogous env/github/code.env does. The harness already sets FULLSEND_FORGE in both env.runner and env.sandbox, so there is no functional impact.

Previous run (13)

Review

Findings

High

  • [api-contract] policies/gitlab/prioritize.yaml:28 — The vertex_ai network policy uses access: read-only for both api.anthropic.com (line 28) and *.googleapis.com (line 33). Every other policy file in the repository (policies/github/prioritize.yaml, policies/gitlab/review.yaml, policies/gitlab/triage.yaml, policies/gitlab/code.yaml) uses access: read-write for these same endpoints. The agent sends inference requests (HTTP POST) to the Anthropic/Vertex AI APIs, which requires write access. With read-only, the GitLab prioritize agent will fail at runtime when it attempts to make inference calls.
    Remediation: Change access: read-only to access: read-write for both vertex_ai endpoints (lines 28 and 33).

Medium

  • [protected-path] This PR modifies 12 files under protected paths (agents/, harness/, policies/, scripts/): agents/prioritize.md, harness/prioritize.yaml, policies/github/prioritize.yaml, policies/gitlab/prioritize.yaml, scripts/lib/github-prioritize-ops.lib.sh, scripts/lib/gitlab-prioritize-ops.lib.sh, scripts/lib/prioritize-ops.lib.sh, scripts/post-prioritize-test.sh, scripts/post-prioritize.sh, scripts/post-prioritize.src.sh, scripts/pre-prioritize.sh, scripts/pre-prioritize.src.sh. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and explains the rationale. Human approval is always required for protected-path changes, regardless of context.

Low

  • [pattern-inconsistency] policies/gitlab/prioritize.yaml:41 — The gitlab_api endpoints omit allow_encoded_slash: true, which is present in policies/gitlab/review.yaml and policies/gitlab/code.yaml. However, policies/gitlab/triage.yaml also omits this flag and works correctly in production, reducing the actual risk. GitLab project paths with nested groups use URL-encoded slashes (group%2Fproject).

  • [pattern-inconsistency] scripts/lib/gitlab-prioritize-ops.lib.sh:26 — The _gitlab_api() function includes an inline host allowlist (case "${GITLAB_HOST}" in gitlab.com|gitlab.cee.redhat.com) that is not present in the equivalent functions in gitlab-triage-ops.lib.sh or gitlab-review-ops.lib.sh. While this is a defense-in-depth addition, it creates maintenance divergence: a new GitLab host must be updated in three places in this file rather than the two required by other agents.

Info

  • [provenance-warning] Prior review context discarded: provenance validation failed (unverifiable-wrong-app). This review treats all findings as first-time assessments.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (14)

Review

Findings

Critical

  • [unresolved-merge-conflict] policies/github/prioritize.yaml:3 — The file contains unresolved git merge conflict markers (<<<<<<<< HEAD:policies/gitlab/review.yaml, ========, >>>>>>>> c555bb1). This makes the YAML syntactically invalid and will fail to parse at runtime, breaking the GitHub forge sandbox policy entirely.
    Remediation: Remove the merge conflict markers and replace the file content with a proper GitHub prioritize policy — use a github_api network policy with api.github.com endpoints and **/gh binary, matching policies/github/review.yaml.

  • [wrong-policy-content] policies/github/prioritize.yaml:30 — The file contains GitLab network policy content: a gitlab_api section with gitlab.com/gitlab.cee.redhat.com endpoints and **/curl binary instead of GitHub API endpoints and **/gh. The GitHub prioritize agent uses gh (via github_csma_run in github-prioritize-ops.lib.sh) to access api.github.com. With this policy, gh is blocked by the binary allowlist and no GitHub API endpoint is permitted — the agent will be unable to post comments or update project fields.
    Remediation: Replace the gitlab_api network policy section with a github_api section matching policies/github/review.yaml: endpoints for api.github.com (REST and GraphQL) and github.com, with binaries **/gh and **/node.

High

  • [unresolved-merge-conflict] policies/gitlab/review.yaml:3 — Merge conflict markers were introduced into the existing GitLab review policy, corrupting a pre-existing, unrelated file. This file is outside the scope of issue Make prioritize agent multi-forge (GitHub + GitLab) #818. The YAML will fail to parse, breaking the GitLab review agent's sandbox configuration.
    Remediation: Revert policies/gitlab/review.yaml to its pre-PR state (the full 58-line policy with # Sandbox policy for the review agent (GitLab forge). comment, without any conflict markers).

Medium

  • [network-policy-over-permission] policies/gitlab/prioritize.yaml:42 — The GitLab prioritize policy grants access: read-write to gitlab.com and gitlab.cee.redhat.com endpoints. The agent prompt states "Do NOT post comments, apply labels, or modify the issue" — mutations are handled by the post-script on the runner, outside the sandbox. Granting read-write to the sandbox contradicts least-privilege.
    Remediation: Change access: read-write to access: read-only for the gitlab_api endpoints.

  • [missing-doc-update] FEATURES.md:94 — The PR introduces scripts/pre-prioritize.src.sh, making scripts/pre-prioritize.sh a generated/bundled script. The exhaustive generated-scripts list in FEATURES.md does not include scripts/pre-prioritize.sh.
    Remediation: Add scripts/pre-prioritize.sh to the generated-scripts list.

  • [protected-path] 13 of 18 changed files are under governance paths (agents/, harness/, policies/, scripts/). The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and the description explains the rationale (multi-forge conversion following established pattern). Human approval is required for protected-path changes regardless of context.

Info

  • [provenance-warning] Prior review context discarded: provenance validation failed (unverifiable-wrong-app). This review treats all findings as first-time assessments.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (15)

Review

Findings

Medium

  • [scope-deviation] scripts/lib/gitlab-prioritize-ops.lib.sh:88 — GitLab forge_update_project_scores() skips the custom fields API call entirely, but issue Make prioritize agent multi-forge (GitHub + GitLab) #818 specifies: "attempt to write scores via the GitLab custom fields API (available on Premium/Ultimate). If the API returns 403/404, it logs a notice and returns success." The function logs a notice and returns 0 without making any API call. The PR body and docs/prioritize.md explicitly document this deferral with a technical rationale (the Issues API ignores unknown keys; the Custom Fields API requires runtime field ID resolution on Premium/Ultimate). Scores remain available in the reasoning comment. This is a documented scope decision, not an accidental omission.
    Remediation: Either (1) implement the graceful degradation attempt as specified in the issue, or (2) update issue Make prioritize agent multi-forge (GitHub + GitLab) #818 to formally authorize deferring GitLab custom fields entirely.

  • [protected-path] agents/, harness/, policies/, scripts/ — This PR modifies 12 files under governance-protected paths: agents/prioritize.md, harness/prioritize.yaml, 2 files under policies/, and 8 files under scripts/. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and explains the rationale (multi-forge conversion following the established triage pattern). Human approval is always required for protected-path changes, regardless of context.

Low

  • [gha-workflow-command-injection] scripts/lib/gitlab-prioritize-ops.lib.sh — The _gha_sanitize function added in this PR is not used for ::error:: outputs in post-prioritize.src.sh, which use ad-hoc sanitization (stripping :: and %0A/%0D but not ANSI escapes). The primary injection vectors are covered; ANSI escapes could clutter log output but cannot create new GHA workflow commands.

  • [missing-test] scripts/pre-prioritize.src.sh — No pre-prioritize-test.sh exists. The validation functions are exercised indirectly in post-prioritize-test.sh (both GitHub and GitLab paths including non-allowlisted host rejection), and no pre-triage-test.sh precedent exists either, so the marginal value is limited.

  • [error-handling] scripts/lib/gitlab-prioritize-ops.lib.sh:97 — In forge_post_sticky_comment, when _gitlab_bot_username fails, the fallback path does _gitlab_api POST ...; return. The bare return propagates the _gitlab_api exit code — if the fallback POST also fails, set -e causes an unhandled script exit. Consistent with the triage agent's identical implementation.

  • [permission-expansion] policies/gitlab/prioritize.yaml:39 — New network policy grants the prioritize agent's curl binary access to gitlab.com and gitlab.cee.redhat.com. Consistent with policies/gitlab/triage.yaml (same hosts, ports, binaries), and the code-level allowlists in _gitlab_api() and forge_validate_issue_url() enforce the same host set.

  • [permission-expansion] agents/prioritize.md:4curl added to the agent tools declaration. On GitHub, the network policy binary allowlist excludes **/curl, preventing network access even though the tool is granted.

Previous run (16)

Review

Findings

High

  • [scope-deviation] scripts/lib/gitlab-prioritize-ops.lib.sh:88 — GitLab forge_update_project_scores() completely defers the custom fields API call, but issue Make prioritize agent multi-forge (GitHub + GitLab) #818 specifies: "attempt to write scores via the GitLab custom fields API (available on Premium/Ultimate). If the API returns 403/404, it logs a notice and returns success — graceful degradation, not failure." The implementation skips the attempt entirely, logging a notice and returning 0 without making any API call. The PR body compounds this by claiming "attempts write on Premium/Ultimate, logs notice and succeeds on Free tier's 403/404" — which is factually incorrect. The code comment documents a legitimate technical rationale (the Issues API ignores unknown keys; the Custom Fields API requires runtime field ID resolution), but this scope reduction was not authorized in the issue.
    Remediation: Either (1) implement the graceful degradation attempt as specified in the issue, or (2) update issue Make prioritize agent multi-forge (GitHub + GitLab) #818 to formally authorize deferring GitLab custom fields entirely and correct the PR body to accurately describe the implementation.

Medium

  • [protected-path] agents/, harness/, policies/, scripts/ — This PR modifies 12 files under governance-protected paths: agents/prioritize.md, harness/prioritize.yaml, 2 files under policies/, and 8 files under scripts/. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and explains the rationale (multi-forge conversion following the established triage pattern). Human approval is always required for protected-path changes, regardless of context.

Low

  • [missing-test] scripts/pre-prioritize.src.sh — No pre-prioritize-test.sh exists. The pre-prioritize script sources prioritize-ops.lib.sh and calls forge_validate_issue_url, adding forge dispatch logic. The validation functions are exercised indirectly in post-prioritize-test.sh (both GitHub and GitLab paths including non-allowlisted host rejection), and no pre-triage-test.sh precedent exists either, so the marginal value is limited.

  • [permission-expansion] policies/gitlab/prioritize.yaml:39 — New network policy grants the prioritize agent's curl binary access to gitlab.com and gitlab.cee.redhat.com. The expansion is consistent with the existing precedent in policies/gitlab/triage.yaml (same hosts, same ports, same binaries), and the code-level allowlists in _gitlab_api() and forge_validate_issue_url() enforce the same host set.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (17)

Review

Findings

Medium

  • [protected-path] agents/, harness/, policies/, scripts/ — This PR modifies 12 files under governance-protected paths: agents/prioritize.md, harness/prioritize.yaml, 2 files under policies/, and 8 files under scripts/. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and explains the rationale (multi-forge conversion following the established triage pattern). Human approval is always required for protected-path changes, regardless of context.

Low

  • [missing-test] scripts/pre-prioritize.src.sh — No pre-prioritize-test.sh exists. The pre-prioritize script now has forge dispatch logic (sourcing prioritize-ops.lib.sh and calling forge_validate_issue_url), which means both GitHub URL validation and GitLab URL validation paths are untested. While the logic is simple, URL validation edge cases could regress silently.

  • [permission-expansion] policies/gitlab/prioritize.yaml:39 — Includes gitlab.cee.redhat.com as an allowed host in the GitLab network policy. The policy comment documents intent ("supported deployment target for enrolled repos"), and the code-level allowlist in both forge_validate_issue_url() and _gitlab_api() is consistent.

  • [incomplete-doc] LOCAL.md:41 — References GITHUB_ISSUE_URL as the external env var for local testing, which is correct for the GitHub forge path. Does not mention the GitLab equivalent (GITLAB_ISSUE_URL, GITLAB_TOKEN, FULLSEND_FORGE="gitlab") for completeness now that prioritize supports both forges.


Labels: PR converts the prioritize agent to multi-forge support, matching the agent-specific and type labeling conventions.

Previous run (18)

Review

Findings

Medium

  • [logic-error] scripts/lib/gitlab-prioritize-ops.lib.sh:155 — GitLab forge_post_comment ignores the marker parameter (_marker is unused). Unlike the GitHub path which uses fullsend post-comment --marker for idempotent comment updates, the GitLab path always POSTs a new note. Repeated prioritize runs on the same GitLab issue will create duplicate RICE score comments instead of updating the existing one. The comment body includes the marker HTML comment (<!-- fullsend:prioritize-agent -->), but no lookup/update logic exists.
    Remediation: Search existing notes for the marker string using GET /projects/:id/issues/:iid/notes and update with PUT if found, or document this as a known limitation.

  • [protected-path] agents/, harness/, policies/, scripts/ — This PR modifies 12 files under governance-protected paths: agents/prioritize.md, harness/prioritize.yaml, 2 files under policies/, and 8 files under scripts/. The PR links to issue Make prioritize agent multi-forge (GitHub + GitLab) #818 and explains the rationale (multi-forge conversion following the established triage pattern). Human approval is always required for protected-path changes, regardless of context.

Low

  • [dead-code] scripts/lib/gitlab-prioritize-ops.lib.sh:33_gitlab_api_with_status is defined but never called. forge_update_project_scores manually inlines its own curl-with-status logic rather than calling this helper.

  • [missing-test] scripts/pre-prioritize.src.sh — No corresponding test file (pre-prioritize-test.sh) exists. The triage agent has pre-triage-test.sh as precedent. The new pre-script sources the forge ops library and calls forge_validate_issue_url with forge-specific validation, which is more complex than the old 4-line inline script.

  • [ssrf] scripts/lib/gitlab-prioritize-ops.lib.sh:22_gitlab_api() uses GITLAB_HOST to construct API URLs without independent host validation. The current call ordering is safe (forge_validate_issue_url runs first, and the sandbox network policy provides a second layer), but an allowlist check in _gitlab_api() itself would provide defense-in-depth.

  • [permission-expansion] policies/gitlab/prioritize.yaml:39 — Includes gitlab.cee.redhat.com as an allowed host. The policy comment documents intent ("supported deployment target for enrolled repos"), and the code-level allowlist in forge_validate_issue_url is consistent.

  • [secrets-handling] env/github/prioritize.env:2GH_TOKEN=${GH_TOKEN} exported without quotes, while ISSUE_URL uses quotes in the same file. Matches pre-existing pattern from the deleted env/prioritize.env.

  • [stale-doc] agents/review.md:29 — References GITHUB_ISSUE_URL, which is correct for the review agent's current GitHub-only implementation. Will need updating when the review agent undergoes its own multi-forge conversion.

  • [stale-doc] LOCAL.md:41 — References GITHUB_ISSUE_URL as the external env var, which is correct for the GitHub forge path (the harness maps it to ISSUE_URL). Could benefit from mentioning the GitLab path for completeness.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 15, 2026
@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from 4c0b4fc to e78a966 Compare August 16, 2026 02:48
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:49 AM UTC · Completed 3:06 AM UTC

Commit: e78a966 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added prioritize-agent enhancement New feature or request labels Aug 16, 2026
@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from e78a966 to 62a82cd Compare August 16, 2026 03:09
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:10 AM UTC · Completed 3:27 AM UTC

Commit: 62a82cd · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot removed the requires-manual-review Review requires human judgment label Aug 16, 2026
@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from 62a82cd to c555bb1 Compare August 16, 2026 03:32
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:33 AM UTC · Completed 3:49 AM UTC

Commit: c555bb1 · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 16, 2026 03:49

Superseded by updated review

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Aug 16, 2026
@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from c555bb1 to 977c53d Compare August 19, 2026 14:48
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:49 PM UTC · Completed 3:09 PM UTC

Commit: 977c53d · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot removed the requires-manual-review Review requires human judgment label Aug 19, 2026
@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ❌ Failure · Started 3:10 PM UTC · Completed 3:18 PM UTC

Commit: 977c53d · View workflow run →

@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from 977c53d to 8bae1df Compare August 19, 2026 15:16
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 3:17 PM UTC · Ended 3:31 PM UTC

Commit: 8bae1df · View workflow run →

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

⚠️ Post-fix script failed — Push rejected (exit code 1)

The fix agent completed, but the post-fix script failed before finishing.

Workflow run: https://github.com/fullsend-ai/agents/actions/runs/32268310274

Details:
To https://github.com/fullsend-ai/agents.git
! [rejected] agent/818-prioritize-multi-forge -> agent/818-prioritize-multi-forge (fetch first)
error: failed to push some refs to 'https://github.com/fullsend-ai/agents.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
To https://github.com/fullsend-ai/agents.git
! [rejected] agent/818-prioritize-multi-forge -> agent/818-prioritize-multi-forge (stale info)
error: failed to push some refs to 'https://github.com/fullsend-ai/agents.git'
Please check the workflow logs for full details and retry with /fs-fix if appropriate.

fullsend-ai-review[bot]

This comment was marked as outdated.

fullsend-ai-review[bot]

This comment was marked as outdated.

@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from a06a7a2 to c752b47 Compare August 19, 2026 19:39
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:40 PM UTC · Completed 7:59 PM UTC

Commit: c752b47 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch 2 times, most recently from 86d00a5 to 8a20fcd Compare August 19, 2026 20:36
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 8:36 PM UTC · Ended 8:37 PM UTC

Commit: 86d00a5 · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:40 PM UTC · Completed 9:01 PM UTC

Commit: 8a20fcd · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from 8a20fcd to fc10f0c Compare August 19, 2026 21:07
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:10 PM UTC · Completed 9:32 PM UTC

Commit: fc10f0c · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from fc10f0c to f97c419 Compare August 19, 2026 21:35
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:37 PM UTC · Completed 9:55 PM UTC

Commit: f97c419 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from f97c419 to 63731e8 Compare August 19, 2026 21:59
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:01 PM UTC · Completed 10:19 PM UTC

Commit: 63731e8 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from 63731e8 to 82e8e59 Compare August 19, 2026 22:23
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:25 PM UTC · Completed 10:43 PM UTC

Commit: 82e8e59 · View workflow run →

fullsend-ai-review[bot]

This comment was marked as outdated.

Signed-off-by: Greg Allen <gallen@redhat.com>
Signed-off-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ggallen
ggallen force-pushed the agent/818-prioritize-multi-forge branch from 82e8e59 to 801469d Compare August 19, 2026 23:12
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:14 PM UTC · Completed 11:34 PM UTC

Commit: 801469d · View workflow run →

@ggallen
ggallen added this pull request to the merge queue Aug 20, 2026
Merged via the queue into main with commit 8612775 Aug 20, 2026
18 checks passed
@ggallen
ggallen deleted the agent/818-prioritize-multi-forge branch August 20, 2026 00:03
@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 20, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ❌ Failure · Started 12:05 AM UTC · Completed 12:05 AM UTC

Commit: 801469d · View workflow run →

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request prioritize-agent ready-for-review requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make prioritize agent multi-forge (GitHub + GitLab)

1 participant